﻿using System;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Compilation;
using FlickrXplorer.Web.Objects;
using FlickrXplorer.Web.Plugin.Exceptions;

namespace FlickrXplorer.Web.Plugin
{
  /// <summary>
  /// Handles the loading and invoking of plugins.
  /// </summary>                                  
  public class PluginManager
  {
    private const string cacheKey = "PluginManager";
    private const string abovePhotoTemplate = "FlickrXplorer.Web.Plugin.Templates.AbovePhoto.txt";
    private const string abovePopularTags = "FlickrXplorer.Web.Plugin.Templates.AbovePopularTags.txt";
 
    /// <summary>
    /// Gets the collection of loaded plugins.
    /// </summary>                            
    public FlickrXplorerPluginCollection Plugins { get; private set; }
    /// <summary>
    /// Gets or sets whether all plugins must be marked with a red
    /// border during rendering.
    /// </summary>                                                
    public bool MarkAllPlugins { get; set; }

    private PluginManager()
    {
      this.Plugins = new FlickrXplorerPluginCollection();
      this.MarkAllPlugins = false;
      this.Initialise();
    }

    /// <summary>
    /// Gets the only instance of the <see cref="FlickrXplorer.Web.Plugin.PluginManager" text="PluginManager Class" />.
    /// </summary>                                                                                                     
    public static PluginManager Current
    {
      get
      {
        if(HttpContext.Current.Application[cacheKey] == null)
        {
          HttpContext.Current.Application[cacheKey] = new PluginManager();
        }

        return HttpContext.Current.Application[cacheKey] as PluginManager;
      }
    }

    /// <summary>
    /// Gets all content to be rendered above the main photograph.
    /// </summary>
    /// <param name="photoDetail">The PhotoDetail object.</param> 
    public string GetAllMainPhotoContent(PhotoDetail photoDetail)
    {
      StringBuilder builder = new StringBuilder();
      this.Plugins.Foreach((FlickrXplorerPlugin plugin) =>
        {
          try
          {
            builder.Append(GetMainPhotoContent(photoDetail, plugin));
          }
          catch (FlickrXplorerPluginException)
          {
            // Ignore this error, we don't want to display plugins that don't render
          }
        });
      return builder.ToString();
    }

    public string GetMainPhotoContent(PhotoDetail photoDetail, FlickrXplorerPlugin plugin)
    {
      string output = string.Empty;

      if (plugin.PluginStatus == PluginStatus.Enabled)
      {
        ContentSection contentSection = new ContentSection(abovePhotoTemplate, TemplateType.EmbeddedResourceFile);
        bool hasOutput = plugin.OnLoadMainPhoto(contentSection, photoDetail);
        if (hasOutput)
        {
          output = WrapPluginOutput(contentSection.ToString(), plugin, PageArea.AbovePhoto);
        }
      }

      return output;
    }

    /// <summary>
    /// Gets all content to be displayed in the sidebar above the
    /// popular tags control.
    /// </summary>
    /// <param name="photoData">The PhotoData object.</param>    
    public string GetAllSidebarContent(PhotoData photoData)
    {
      StringBuilder builder = new StringBuilder();
      this.Plugins.Foreach((FlickrXplorerPlugin plugin) =>
      {
        try
        {
          builder.Append(GetSidebarContent(photoData, plugin));
        }
        catch (FlickrXplorerPluginException)
        {
          // Ignore this error, we don't want to display plugins that don't render
        }
      });
      return builder.ToString();
    }

    public string GetSidebarContent(PhotoData photoData, FlickrXplorerPlugin plugin)
    {
      string output = string.Empty;

      if (plugin.PluginStatus == PluginStatus.Enabled)
      {
        ContentSection contentSection = new ContentSection(abovePopularTags, TemplateType.EmbeddedResourceFile);
        bool hasOutput = plugin.OnLoadSideBar(contentSection, photoData);
        if (hasOutput)
        {
          output = WrapPluginOutput(contentSection.ToString(), plugin, PageArea.AbovePhoto);
        }
      }

      return output;
    }

    private string WrapPluginOutput(string content,
      FlickrXplorerPlugin plugin,
      PageArea location)
    {
      StringBuilder builder = new StringBuilder();
      string beforeContent = this.MarkAllPlugins ?
        "<!-- START: Plugin: {0}, Location: {1} --><div style='border: 1px solid red'>" :
        "<!-- START: Plugin: {0}, Location: {1} -->";
      string afterContent = this.MarkAllPlugins ?
        "</div><!-- END: Plugin: {0}, Location: {1} -->" :
        "<!-- END: Plugin: {0}, Location: {1} -->";

      builder.AppendFormat(beforeContent,
        plugin.Name,
        location.ToString());

      builder.Append(content);

      builder.AppendFormat(afterContent,
        plugin.Name,
        location.ToString());

      return builder.ToString();
    }

    /// <summary>
    /// Initialises the <see cref="FlickrXplorer.Web.Plugin.PluginManager" text="PluginManager Class" />.
    /// </summary>
    /// <remarks>
    /// This is when all plugins are loaded into the Application
    /// Domain.
    /// </remarks>                                                                                       
    public void Initialise()
    {
      var assemblies = BuildManager
        .GetReferencedAssemblies()
        .Cast<Assembly>();

      // Forcing only the demo assembly for speed purposes.
      foreach (Assembly assembly in assemblies.Where(p => p.FullName.Contains("MyFlickrXplorerPlugins")))
      {
        try
        {
          Type[] assemblyTypes = assembly.GetTypes();

          foreach (Type type in assemblyTypes)
          {
            if (IsFlickrXplorerPlugin(type))
            {
              FlickrXplorerPlugin plugin = (FlickrXplorerPlugin)Activator.CreateInstance(type);
              this.Plugins.Add(plugin);
            }
          }

          break;
        }
        catch (ReflectionTypeLoadException)
        {
          // Ignore this exception
        }
      }
    }

    private bool IsFlickrXplorerPlugin(Type type)
    {
      return (typeof(FlickrXplorerPlugin).IsAssignableFrom(type) 
        && type != typeof(FlickrXplorerPlugin)
        && !type.IsAbstract);
    }
  }
}
